/**
 * Copyright (c) 2011 Nokia Corporation.
 */

// Own header
#include "filemanager.h"

// Qt includes
#include <QFileInfo>

#ifdef Q_OS_WIN
    #include <windows.h>
#endif

// Backup2Go includes
#include "drivemodel.h"
#include "filemodel.h"
#include "trace.h"

QTM_USE_NAMESPACE;


/*!
  \class FileManager
  \brief Implements a simple interface to manage a file system.
*/


/*!
  Constructor.
*/
FileManager::FileManager(QObject *parent /* = 0 */)
    : m_sysStorageInfo(0),
      m_driveModel(0)
{
    initialize();
}


/*!
  Constructor.
*/
FileManager::FileManager(DriveModel *driveModel, QObject *parent /* = 0 */)
    : m_sysStorageInfo(0),
      m_driveModel(driveModel)
{
    initialize();
}


/*!
  Performs the initial operations after construction.
*/
void FileManager::initialize()
{
    m_sysStorageInfo = new QSystemStorageInfo();

    connect(m_sysStorageInfo, SIGNAL(logicalDriveChanged(bool, QString)),
            this, SLOT(updateDrives(bool, QString)));

    updateDrives();
}


/*!
  Destructor.
*/
FileManager::~FileManager()
{
    delete m_sysStorageInfo;
}


/*!
  Sets the drive model.
*/
void FileManager::setDriveModel(DriveModel *model)
{
    m_driveModel = model;
    updateDrives();
}


/*!
  Populates \a model with the contents of \a dir.
*/
void FileManager::populateModel(FileModel &model, QDir dir) const
{
    DEBUG_INFO(dir.dirName());
    model.setFiles(dir.entryInfoList());
}


/*!
    Retrieves the content from \a dir into \a files. If \a recursive is true,
    will traverse all the sub folders recursively.

    Note that if the folder structure is large, this method will jam the UI if
    not ran in a separate thread!
*/
void FileManager::getFolderContent(QDir dir,
                                   QList<QFileInfo> &files,
                                   bool recursive /* = true */) const
{
    QFileInfoList fileInfoList =
        dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files);

    DEBUG_INFO("Folder" << dir.absolutePath() << "contains" <<
               fileInfoList.count() << "items of interest.");

    QFileInfo fileInfo;

    foreach (fileInfo, fileInfoList) {
        if (fileInfo.isSymLink()) {
            // Do not add symbolic links to the list.
            continue;
        }

        DEBUG_INFO("Appending" << fileInfo.absoluteFilePath());
        files.append(fileInfo);

        if (fileInfo.isDir() && recursive) {
            // Traverse the folder.
            QDir newDir(fileInfo.filePath());
            getFolderContent(newDir, files, true);
        }
    }
}


/*!
  Returns the free space on the drive specified by \a driveVolume.
*/
qlonglong FileManager::freeSpaceOnDrive(const QString &driveVolume) const
{
#ifdef Q_OS_WIN
    DEBUG_INFO("Using Windows.");

    std::string volume = driveVolume.toStdString();
    ULARGE_INTEGER freeSpace, totalSpace;

    bool success = ::GetDiskFreeSpaceExA(volume.c_str(),
                                         &freeSpace,
                                         &totalSpace,
                                         NULL);

    if (!success) {
        DEBUG_INFO("GetDiskFreeSpaceExA() failed with drive" << driveVolume);
        return -1;
    }

    return static_cast<qlonglong>(static_cast<__int64>(freeSpace.QuadPart));
#else
    return m_sysStorageInfo->availableDiskSpace(driveVolume);
#endif
}


/*!
  Updates the drive information list. This slot could have been invoked by
  QSystemStorageInfo::logicalDriveChanged() signal and if so \a vol may contain
  a new drive.
*/
void FileManager::updateDrives(bool added /* = false */,
                               const QString &vol /* = QString() */)
{
    DEBUG_INFO((int)added << "," << vol);

#ifdef Q_OS_WIN
    m_driveModel->clear();

    QFileInfoList drives = QDir::drives();
    QFileInfo fileInfo;

    foreach (fileInfo, drives) {
        DriveInfo info;
        info.m_vol = fileInfo.absoluteFilePath();

        if (m_sysStorageInfo) {
            info.m_type = m_sysStorageInfo->typeForDrive(info.m_vol);

            if (info.m_type == QSystemStorageInfo::NoDrive) {
                // Failed to identify the type of the drive.
                // Let's use undefined.
                info.m_type = BTGUndefined;
            }
        }

        DEBUG_INFO(info.m_vol << "," << (int)info.m_type);

        m_driveModel->appendDrive(info);
    }
#else
    if (added && !vol.isEmpty()) {
        // Add the given drive if not in the list already.
        DriveInfo info;
        info.m_vol = vol;
        info.m_type = m_sysStorageInfo->typeForDrive(vol);
        m_driveModel->appendDrive(info);
    }
    else {
        // Clear the list and query all available drives and append them
        // into the list.
        m_driveModel->clear();
        QStringList drives = m_sysStorageInfo->logicalDrives();
        QString volume;

        foreach (volume, drives) {
            DriveInfo info;
            info.m_vol = volume;
            info.m_type = m_sysStorageInfo->typeForDrive(volume);

            DEBUG_INFO(info.m_vol << "," << (int)info.m_type);

            m_driveModel->appendDrive(info);
        }
    }
#endif

    DEBUG_INFO(m_driveModel->rowCount() << "drives available.");
}


// End of file.
